package com.shutuo.callnummeiwei.socket;

import android.text.TextUtils;
import android.util.Log;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.util.concurrent.TimeUnit;

import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;

public class SocketCommunicateManager {
    private static final String BROADCAST_ADDRESS = "255.255.255.255";
    private static final int SEND_PORT = 9901;
    private static final int REC_PORT = 9903;
    private static final String TELLMEIPMSG = "tell me your ip";

    private static SocketCommunicateManager instance;
    private DatagramSocket mDatagramSocket = null;
    private static boolean isReceiverMgs = false;
    private Disposable keepMgsDisposable;
    private Disposable getHostIpDisposable;

    private SocketCommunicateManager() {
        init();
    }

    public static SocketCommunicateManager getInstance() {
        if (instance == null) {
            synchronized (SocketCommunicateManager.class) {
                if (instance == null) {
                    instance = new SocketCommunicateManager();
                }
            }
        }
        return instance;
    }

    private void init() {
        Log.d("lk", "[SocketCommunicateManager] init");
        try {
            mDatagramSocket = new DatagramSocket(null);
            mDatagramSocket.setReuseAddress(true);
            mDatagramSocket.setBroadcast(true);
            mDatagramSocket.bind(new InetSocketAddress(REC_PORT));
            receiveUdpMessage();
        } catch (SocketException e) {
            e.printStackTrace();
        }
    }

    public void startSendMsg() {
        Log.d("lk", "[SocketCommunicateManager] startSendMsg");
        getHostIp();
    }

    public void stopReceiveMsg() {
        isReceiverMgs = true;
        mDatagramSocket.close();
        if (keepMgsDisposable != null && !keepMgsDisposable.isDisposed()) {
            keepMgsDisposable.dispose();
        }
    }

    private void getHostIp() {
        Log.d("lk", "[SocketCommunicateManager] getHostIp");

        getHostIpDisposable = Observable.intervalRange(0, 10, 0, 10, TimeUnit.SECONDS).observeOn(Schedulers.io()).subscribe(new Consumer<Long>() {
            @Override
            public void accept(Long aLong) throws Exception {
                Log.d("lk", "[SocketCommunicateManager] getHostIp accept");
                sendUdpMessage(BROADCAST_ADDRESS, TELLMEIPMSG);
            }
        });

    }

    private void sendUdpMessage(final String hostName, final String msg) {
        Log.d("lk", "[SocketCommunicateManager] sendUdpMessage hostName:" + hostName + "  " + "msg:" + msg);
        if (TextUtils.isEmpty(msg)) {
            return;
        }
        Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> emitter) throws Exception {
                Log.d("lk", "[SocketCommunicateManager] sendUdpMessage Observable subscribe");
                emitter.onNext("start");
                emitter.onComplete();
            }
        }).observeOn(Schedulers.io()).subscribe(new Consumer<String>() {
            @Override
            public void accept(String string) throws Exception {
                Log.d("lk", "[SocketCommunicateManager] sendUdpMessage Observable accept string:" + string);
                try {
                    mDatagramSocket.setBroadcast(true);
                    InetAddress address = InetAddress.getByName(hostName);
                    DatagramPacket datagramPacket = new DatagramPacket(msg.getBytes(), msg.getBytes().length, address, SEND_PORT);
                    Log.d("lk", "[SocketCommunicateManager] sendMessage message:" + msg);
                    mDatagramSocket.send(datagramPacket);
                } catch (Exception e) {
                    Log.e("stcLogUdp", "[SeedMessage] Exception:" + e);
                }
            }
        });
    }


    private void sendKeepMgs(final String hostName) {
        Log.d("lk", "[SocketCommunicateManager] sendKeepMgs hostName:" + hostName);
        final JSONObject jsonObject = new JSONObject();
        try {
            jsonObject.put("MsgType", 7);
            jsonObject.put("V", 1);
            jsonObject.put("Type", 8);
            jsonObject.put("Token", "da2e5f4eeac4b7c5069eae3d503490623d5d5c76");
        } catch (JSONException e) {
            e.printStackTrace();
        }

        keepMgsDisposable = Observable.interval(5, TimeUnit.SECONDS).observeOn(Schedulers.io()).subscribe(new Consumer<Long>() {
            @Override
            public void accept(Long aLong) throws Exception {
                sendUdpMessage(hostName, jsonObject.toString());
            }
        });
    }


    private void receiveUdpMessage() {
        Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> emitter) throws Exception {
                byte[] message = new byte[2048];
                DatagramPacket mDatagramPacket = new DatagramPacket(message, message.length);
                while (!isReceiverMgs) {
                    try {
                        mDatagramSocket.receive(mDatagramPacket);
                        String strings = new String(mDatagramPacket.getData(), "GB2312").trim();
                        if (strings != null && strings.contains("my ip is")) {
                            String ip = mDatagramPacket.getAddress().getHostAddress();
                            if (!TextUtils.isEmpty(ip) && !getHostIpDisposable.isDisposed()) {
                                getHostIpDisposable.dispose();
                            }
                            sendKeepMgs(ip);
                        }

                        Log.d("lk", "[SocketCommunicateManager] [receiveUdpMessage] Accept msg：" + strings);
                        emitter.onNext(strings);
                    } catch (Exception e) {
                        Log.e("lk", "[SocketCommunicateManager] [receiveUdpMessage] Exception:" + e);
                        emitter.onError(e);
                    }
                }
//                emitter.onComplete();

            }
        }).subscribeOn(Schedulers.single()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer<String>() {
            @Override
            public void accept(String s) throws Exception {
                Log.d("lk", "[SocketCommunicateManager] [receiveUdpMessage] accept：" + s);
                if (!TextUtils.isEmpty(s) && !s.contains("my ip is")) {
                    if (mMeiWeiDataChangeListner != null) {
                        mMeiWeiDataChangeListner.meiWeiDataChange(parseData(s));
                    }

                }
            }
        }, new Consumer<Throwable>() {
            @Override
            public void accept(Throwable throwable) throws Exception {
                Log.d("lk", "[SocketCommunicateManager] [receiveUdpMessage] throwable msg：" + throwable);
            }
        });
    }


    /**
     * {
     * "GroupEnable": false,
     * "Queues": [
     * {
     * "ID": "267664",
     * "Name": "普通1",
     * "Min": "1-3人",
     * "QuePrefix": "A",
     * "QueMax": 3,
     * "QueMin": 1,
     * "VIP": 0,
     * "WaitN": 2,
     * "Number": "A0",
     * "Calling": ["A1"],
     * "LastNum": "A2"
     * },
     * {
     * "ID": "267665",
     * "Name": "普通2",
     * "Min": "4-5人",
     * "QuePrefix": "B",
     * "QueMax": 5,
     * "QueMin": 4,
     * "VIP": 0,
     * "WaitN": 2,
     * "Number": "B0",
     * "Calling": ["B1"],
     * "LastNum": "B2"
     * },
     * {
     * "ID": "267681",
     * "Name": "特大桌",
     * "Min": "7人+",
     * "QuePrefix": "I",
     * "QueMax": 0,
     * "QueMin": 7,
     * "VIP": 7,
     * "WaitN": 2,
     * "Number": "I0",
     * "Calling": ["I1"],
     * "LastNum": "I2"
     * }
     * ],
     * "CT": 0,
     * "T": 11,
     * "SessionID": "6c8f5a5d2f31d62db60707cc4c9f25085ad2cbcb",
     * "ShopID": "94716",
     * "ShopName": "美味体验店(周洁店)",
     * "ShopAD": "使用微信、我查查等扫描排号单上二维码，可查看排队进展或下载\u201d美味不用等\u201dAPP也可微信关注\u201c美味不用等\u201d公众账号，输入编号后，随时查看您的最新排队进展并获得实时提醒哦！\u201c美味不用等\u201dAPP,手机上的不等位神器 在家取号，到号提醒，提前点菜",
     * "Code": "http://www.mwee.cn/d?94716",
     * "HideWaitN": 0,
     * "MsgType": 8,
     * "DanmuEn": 0
     * }
     */

    private JSONObject parseData(String data) {
        JSONObject jo = new JSONObject();
        try {
            JSONObject dataJO = new JSONObject(data);
            JSONArray queuesJA = dataJO.getJSONArray("Queues");
            for (int i = 0; i < queuesJA.length(); i++) {
                JSONObject jsonObject = queuesJA.optJSONObject(i);

                if (jsonObject.optInt("QueMin") <= 1) {
                    jo.put("smallWaitTable", jsonObject.optInt("WaitN"));
                    JSONArray callingJA = jsonObject.optJSONArray("Calling");
                    if (callingJA != null && callingJA.length() > 0) {
                        jo.put("smallTable", callingJA.optString(0));
                    }
                }

                if (jsonObject.optInt("QueMin") > 1 && jsonObject.optInt("QueMax") != 0) {
                    jo.put("inWaitTable", jsonObject.optInt("WaitN"));
                    JSONArray callingJA = jsonObject.optJSONArray("Calling");
                    if (callingJA != null && callingJA.length() > 0) {
                        jo.put("inTable", callingJA.optString(0));
                    }
                }
                if (jsonObject.optInt("QueMin") > 1 && jsonObject.optInt("QueMax") == 0) {
                    jo.put("bigWaitTable", jsonObject.optInt("WaitN"));
                    JSONArray callingJA = jsonObject.optJSONArray("Calling");
                    if (callingJA != null && callingJA.length() > 0) {
                        jo.put("bigTable", callingJA.optString(0));
                    }
                }
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        Log.d("lk", "[SocketCommunicateManager] [parseData] jo：" + jo.toString());
        return jo;

    }
//    private JSONObject parseData(String data) {
//        JSONObject jo = new JSONObject();
//        try {
//            JSONObject dataJO = new JSONObject(data);
//            JSONArray queuesJA = dataJO.getJSONArray("Queues");
//            for (int i = 0; i < queuesJA.length(); i++) {
//                JSONObject jsonObject = queuesJA.optJSONObject(i);
//                if (TextUtils.equals(jsonObject.optString("Name"), "小桌")) {
//                    jo.put("smallWaitTable", jsonObject.optInt("WaitN"));
//                    JSONArray callingJA = jsonObject.optJSONArray("Calling");
//                    if (callingJA != null && callingJA.length() > 0) {
//                        jo.put("smallTable", callingJA.optString(0));
//                    }
//                }
//                if (TextUtils.equals(jsonObject.optString("Name"), "中桌")) {
//                    jo.put("inWaitTable", jsonObject.optInt("WaitN"));
//                    JSONArray callingJA = jsonObject.optJSONArray("Calling");
//                    if (callingJA != null && callingJA.length() > 0) {
//                        jo.put("inTable", callingJA.optString(0));
//                    }
//                }
//                if (TextUtils.equals(jsonObject.optString("Name"), "大桌")) {
//                    jo.put("bigWaitTable", jsonObject.optInt("WaitN"));
//                    JSONArray callingJA = jsonObject.optJSONArray("Calling");
//                    if (callingJA != null && callingJA.length() > 0) {
//                        jo.put("bigTable", callingJA.optString(0));
//                    }
//                }
//            }
//        } catch (JSONException e) {
//            e.printStackTrace();
//        }
//        Log.d("lk", "[SocketCommunicateManager] [parseData] jo：" + jo.toString());
//        return jo;
//
//    }

    private MeiWeiDataChangeListner mMeiWeiDataChangeListner;

    public void setMeiWeiDataChangeListner(MeiWeiDataChangeListner listner) {
        mMeiWeiDataChangeListner = listner;
    }

    public interface MeiWeiDataChangeListner {
        void meiWeiDataChange(JSONObject str);
    }
}
